/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.awt.event;

import java.awt.Component;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Toolkit;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.awt.IllegalComponentStateException;
import java.awt.MouseInfo;
import sun.awt.SunToolkit;

/**
 * An event which indicates that a mouse action occurred in a component.
 * A mouse action is considered to occur in a particular component if and only
 * if the mouse cursor is over the unobscured part of the component's bounds
 * when the action happens.
 * For lightweight components, such as Swing's components, mouse events
 * are only dispatched to the component if the mouse event type has been
 * enabled on the component. A mouse event type is enabled by adding the
 * appropriate mouse-based {@code EventListener} to the component
 * ({@link MouseListener} or {@link MouseMotionListener}), or by invoking
 * {@link Component#enableEvents(long)} with the appropriate mask parameter
 * ({@code AWTEvent.MOUSE_EVENT_MASK} or {@code AWTEvent.MOUSE_MOTION_EVENT_MASK}).
 * If the mouse event type has not been enabled on the component, the
 * corresponding mouse events are dispatched to the first ancestor that
 * has enabled the mouse event type.
 * <p>
 * For example, if a {@code MouseListener} has been added to a component, or
 * {@code enableEvents(AWTEvent.MOUSE_EVENT_MASK)} has been invoked, then all
 * the events defined by {@code MouseListener} are dispatched to the component.
 * On the other hand, if a {@code MouseMotionListener} has not been added and
 * {@code enableEvents} has not been invoked with
 * {@code AWTEvent.MOUSE_MOTION_EVENT_MASK}, then mouse motion events are not
 * dispatched to the component. Instead the mouse motion events are
 * dispatched to the first ancestors that has enabled mouse motion
 * events.
 * <P>
 * This low-level event is generated by a component object for:
 * <ul>
 * <li>Mouse Events
 * <ul>
 * <li>a mouse button is pressed
 * <li>a mouse button is released
 * <li>a mouse button is clicked (pressed and released)
 * <li>the mouse cursor enters the unobscured part of component's geometry
 * <li>the mouse cursor exits the unobscured part of component's geometry
 * </ul>
 * <li> Mouse Motion Events
 * <ul>
 * <li>the mouse is moved
 * <li>the mouse is dragged
 * </ul>
 * </ul>
 * <P>
 * A <code>MouseEvent</code> object is passed to every
 * <code>MouseListener</code>
 * or <code>MouseAdapter</code> object which is registered to receive
 * the "interesting" mouse events using the component's
 * <code>addMouseListener</code> method.
 * (<code>MouseAdapter</code> objects implement the
 * <code>MouseListener</code> interface.) Each such listener object
 * gets a <code>MouseEvent</code> containing the mouse event.
 * <P>
 * A <code>MouseEvent</code> object is also passed to every
 * <code>MouseMotionListener</code> or
 * <code>MouseMotionAdapter</code> object which is registered to receive
 * mouse motion events using the component's
 * <code>addMouseMotionListener</code>
 * method. (<code>MouseMotionAdapter</code> objects implement the
 * <code>MouseMotionListener</code> interface.) Each such listener object
 * gets a <code>MouseEvent</code> containing the mouse motion event.
 * <P>
 * When a mouse button is clicked, events are generated and sent to the
 * registered <code>MouseListener</code>s.
 * The state of modal keys can be retrieved using {@link InputEvent#getModifiers}
 * and {@link InputEvent#getModifiersEx}.
 * The button mask returned by {@link InputEvent#getModifiers} reflects
 * only the button that changed state, not the current state of all buttons.
 * (Note: Due to overlap in the values of ALT_MASK/BUTTON2_MASK and
 * META_MASK/BUTTON3_MASK, this is not always true for mouse events involving
 * modifier keys).
 * To get the state of all buttons and modifier keys, use
 * {@link InputEvent#getModifiersEx}.
 * The button which has changed state is returned by {@link MouseEvent#getButton}
 * <P>
 * For example, if the first mouse button is pressed, events are sent in the
 * following order:
 * <PRE>
 * <b   >id           </b   >   <b   >modifiers   </b   > <b   >button </b   >
 * <code>MOUSE_PRESSED</code>:  <code>BUTTON1_MASK</code> <code>BUTTON1</code>
 * <code>MOUSE_RELEASED</code>: <code>BUTTON1_MASK</code> <code>BUTTON1</code>
 * <code>MOUSE_CLICKED</code>:  <code>BUTTON1_MASK</code> <code>BUTTON1</code>
 * </PRE>
 * When multiple mouse buttons are pressed, each press, release, and click
 * results in a separate event.
 * <P>
 * For example, if the user presses <b>button 1</b> followed by
 * <b>button 2</b>, and then releases them in the same order,
 * the following sequence of events is generated:
 * <PRE>
 * <b   >id           </b   >   <b   >modifiers   </b   > <b   >button </b   >
 * <code>MOUSE_PRESSED</code>:  <code>BUTTON1_MASK</code> <code>BUTTON1</code>
 * <code>MOUSE_PRESSED</code>:  <code>BUTTON2_MASK</code> <code>BUTTON2</code>
 * <code>MOUSE_RELEASED</code>: <code>BUTTON1_MASK</code> <code>BUTTON1</code>
 * <code>MOUSE_CLICKED</code>:  <code>BUTTON1_MASK</code> <code>BUTTON1</code>
 * <code>MOUSE_RELEASED</code>: <code>BUTTON2_MASK</code> <code>BUTTON2</code>
 * <code>MOUSE_CLICKED</code>:  <code>BUTTON2_MASK</code> <code>BUTTON2</code>
 * </PRE>
 * If <b>button 2</b> is released first, the
 * <code>MOUSE_RELEASED</code>/<code>MOUSE_CLICKED</code> pair
 * for <code>BUTTON2_MASK</code> arrives first,
 * followed by the pair for <code>BUTTON1_MASK</code>.
 * <p>
 * Some extra mouse buttons are added to extend the standard set of buttons
 * represented by the following constants:{@code BUTTON1}, {@code BUTTON2}, and {@code BUTTON3}.
 * Extra buttons have no assigned {@code BUTTONx}
 * constants as well as their button masks have no assigned {@code BUTTONx_DOWN_MASK}
 * constants. Nevertheless, ordinal numbers starting from 4 may be
 * used as button numbers (button ids). Values obtained by the
 * {@link InputEvent#getMaskForButton(int) getMaskForButton(button)} method may be used
 * as button masks.
 * <p>
 * {@code MOUSE_DRAGGED} events are delivered to the {@code Component}
 * in which the mouse button was pressed until the mouse button is released
 * (regardless of whether the mouse position is within the bounds of the
 * {@code Component}).  Due to platform-dependent Drag&amp;Drop implementations,
 * {@code MOUSE_DRAGGED} events may not be delivered during a native
 * Drag&amp;Drop operation.
 *
 * In a multi-screen environment mouse drag events are delivered to the
 * <code>Component</code> even if the mouse position is outside the bounds of the
 * <code>GraphicsConfiguration</code> associated with that
 * <code>Component</code>. However, the reported position for mouse drag events
 * in this case may differ from the actual mouse position:
 * <ul>
 * <li>In a multi-screen environment without a virtual device:
 * <br>
 * The reported coordinates for mouse drag events are clipped to fit within the
 * bounds of the <code>GraphicsConfiguration</code> associated with
 * the <code>Component</code>.
 * <li>In a multi-screen environment with a virtual device:
 * <br>
 * The reported coordinates for mouse drag events are clipped to fit within the
 * bounds of the virtual device associated with the <code>Component</code>.
 * </ul>
 * <p>
 * An unspecified behavior will be caused if the {@code id} parameter
 * of any particular {@code MouseEvent} instance is not
 * in the range from {@code MOUSE_FIRST} to {@code MOUSE_LAST}-1
 * ({@code MOUSE_WHEEL} is not acceptable).
 *
 * @author Carl Quinn
 * @see MouseAdapter
 * @see MouseListener
 * @see MouseMotionAdapter
 * @see MouseMotionListener
 * @see MouseWheelListener
 * @see <a href="https://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html">Tutorial:
 * Writing a Mouse Listener</a>
 * @see <a href="https://docs.oracle.com/javase/tutorial/uiswing/events/mousemotionlistener.html">Tutorial:
 * Writing a Mouse Motion Listener</a>
 * @since 1.1
 */
public class MouseEvent extends InputEvent {

  /**
   * The first number in the range of ids used for mouse events.
   */
  public static final int MOUSE_FIRST = 500;

  /**
   * The last number in the range of ids used for mouse events.
   */
  public static final int MOUSE_LAST = 507;

  /**
   * The "mouse clicked" event. This <code>MouseEvent</code>
   * occurs when a mouse button is pressed and released.
   */
  public static final int MOUSE_CLICKED = MOUSE_FIRST;

  /**
   * The "mouse pressed" event. This <code>MouseEvent</code>
   * occurs when a mouse button is pushed down.
   */
  public static final int MOUSE_PRESSED = 1 + MOUSE_FIRST; //Event.MOUSE_DOWN

  /**
   * The "mouse released" event. This <code>MouseEvent</code>
   * occurs when a mouse button is let up.
   */
  public static final int MOUSE_RELEASED = 2 + MOUSE_FIRST; //Event.MOUSE_UP

  /**
   * The "mouse moved" event. This <code>MouseEvent</code>
   * occurs when the mouse position changes.
   */
  public static final int MOUSE_MOVED = 3 + MOUSE_FIRST; //Event.MOUSE_MOVE

  /**
   * The "mouse entered" event. This <code>MouseEvent</code>
   * occurs when the mouse cursor enters the unobscured part of component's
   * geometry.
   */
  public static final int MOUSE_ENTERED = 4 + MOUSE_FIRST; //Event.MOUSE_ENTER

  /**
   * The "mouse exited" event. This <code>MouseEvent</code>
   * occurs when the mouse cursor exits the unobscured part of component's
   * geometry.
   */
  public static final int MOUSE_EXITED = 5 + MOUSE_FIRST; //Event.MOUSE_EXIT

  /**
   * The "mouse dragged" event. This <code>MouseEvent</code>
   * occurs when the mouse position changes while a mouse button is pressed.
   */
  public static final int MOUSE_DRAGGED = 6 + MOUSE_FIRST; //Event.MOUSE_DRAG

  /**
   * The "mouse wheel" event.  This is the only <code>MouseWheelEvent</code>.
   * It occurs when a mouse equipped with a wheel has its wheel rotated.
   *
   * @since 1.4
   */
  public static final int MOUSE_WHEEL = 7 + MOUSE_FIRST;

  /**
   * Indicates no mouse buttons; used by {@link #getButton}.
   *
   * @since 1.4
   */
  public static final int NOBUTTON = 0;

  /**
   * Indicates mouse button #1; used by {@link #getButton}.
   *
   * @since 1.4
   */
  public static final int BUTTON1 = 1;

  /**
   * Indicates mouse button #2; used by {@link #getButton}.
   *
   * @since 1.4
   */
  public static final int BUTTON2 = 2;

  /**
   * Indicates mouse button #3; used by {@link #getButton}.
   *
   * @since 1.4
   */
  public static final int BUTTON3 = 3;

  /**
   * The mouse event's x coordinate.
   * The x value is relative to the component that fired the event.
   *
   * @serial
   * @see #getX()
   */
  int x;

  /**
   * The mouse event's y coordinate.
   * The y value is relative to the component that fired the event.
   *
   * @serial
   * @see #getY()
   */
  int y;

  /**
   * The mouse event's x absolute coordinate.
   * In a virtual device multi-screen environment in which the
   * desktop area could span multiple physical screen devices,
   * this coordinate is relative to the virtual coordinate system.
   * Otherwise, this coordinate is relative to the coordinate system
   * associated with the Component's GraphicsConfiguration.
   *
   * @serial
   */
  private int xAbs;

  /**
   * The mouse event's y absolute coordinate.
   * In a virtual device multi-screen environment in which the
   * desktop area could span multiple physical screen devices,
   * this coordinate is relative to the virtual coordinate system.
   * Otherwise, this coordinate is relative to the coordinate system
   * associated with the Component's GraphicsConfiguration.
   *
   * @serial
   */
  private int yAbs;

  /**
   * Indicates the number of quick consecutive clicks of
   * a mouse button.
   * clickCount will be valid for only three mouse events :<BR>
   * <code>MOUSE_CLICKED</code>,
   * <code>MOUSE_PRESSED</code> and
   * <code>MOUSE_RELEASED</code>.
   * For the above, the <code>clickCount</code> will be at least 1.
   * For all other events the count will be 0.
   *
   * @serial
   * @see #getClickCount()
   */
  int clickCount;

  /**
   * Indicates which, if any, of the mouse buttons has changed state.
   *
   * The valid values are ranged from 0 to the value returned by the {@link
   * java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()} method. This range
   * already includes constants {@code NOBUTTON}, {@code BUTTON1}, {@code BUTTON2}, and {@code
   * BUTTON3} if these buttons are present. So it is allowed to use these constants too. For
   * example, for a mouse with two buttons this field may contain the following values: <ul> <li> 0
   * ({@code NOBUTTON}) <li> 1 ({@code BUTTON1}) <li> 2 ({@code BUTTON2}) </ul> If a mouse has 5
   * buttons, this field may contain the following values: <ul> <li> 0 ({@code NOBUTTON}) <li> 1
   * ({@code BUTTON1}) <li> 2 ({@code BUTTON2}) <li> 3 ({@code BUTTON3}) <li> 4 <li> 5 </ul> If
   * support for extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled()} disabled by
   * Java then the field may not contain the value larger than {@code BUTTON3}.
   *
   * @serial
   * @see #getButton()
   * @see java.awt.Toolkit#areExtraMouseButtonsEnabled()
   */
  int button;

  /**
   * A property used to indicate whether a Popup Menu
   * should appear  with a certain gestures.
   * If <code>popupTrigger</code> = <code>false</code>,
   * no popup menu should appear.  If it is <code>true</code>
   * then a popup menu should appear.
   *
   * @serial
   * @see java.awt.PopupMenu
   * @see #isPopupTrigger()
   */
  boolean popupTrigger = false;

  /*
   * JDK 1.1 serialVersionUID
   */
  private static final long serialVersionUID = -991214153494842848L;

  /**
   * A number of buttons available on the mouse at the {@code Toolkit} machinery startup.
   */
  private static int cachedNumberOfButtons;

  static {
        /* ensure that the necessary native libraries are loaded */
    NativeLibLoader.loadLibraries();
    if (!GraphicsEnvironment.isHeadless()) {
      initIDs();
    }
    final Toolkit tk = Toolkit.getDefaultToolkit();
    if (tk instanceof SunToolkit) {
      cachedNumberOfButtons = ((SunToolkit) tk).getNumberOfButtons();
    } else {
      //It's expected that some toolkits (Headless,
      //whatever besides SunToolkit) could also operate.
      cachedNumberOfButtons = 3;
    }
  }

  /**
   * Initialize JNI field and method IDs for fields that may be
   * accessed from C.
   */
  private static native void initIDs();

  /**
   * Returns the absolute x, y position of the event.
   * In a virtual device multi-screen environment in which the
   * desktop area could span multiple physical screen devices,
   * these coordinates are relative to the virtual coordinate system.
   * Otherwise, these coordinates are relative to the coordinate system
   * associated with the Component's GraphicsConfiguration.
   *
   * @return a <code>Point</code> object containing the absolute  x and y coordinates.
   * @see java.awt.GraphicsConfiguration
   * @since 1.6
   */
  public Point getLocationOnScreen() {
    return new Point(xAbs, yAbs);
  }

  /**
   * Returns the absolute horizontal x position of the event.
   * In a virtual device multi-screen environment in which the
   * desktop area could span multiple physical screen devices,
   * this coordinate is relative to the virtual coordinate system.
   * Otherwise, this coordinate is relative to the coordinate system
   * associated with the Component's GraphicsConfiguration.
   *
   * @return x  an integer indicating absolute horizontal position.
   * @see java.awt.GraphicsConfiguration
   * @since 1.6
   */
  public int getXOnScreen() {
    return xAbs;
  }

  /**
   * Returns the absolute vertical y position of the event.
   * In a virtual device multi-screen environment in which the
   * desktop area could span multiple physical screen devices,
   * this coordinate is relative to the virtual coordinate system.
   * Otherwise, this coordinate is relative to the coordinate system
   * associated with the Component's GraphicsConfiguration.
   *
   * @return y  an integer indicating absolute vertical position.
   * @see java.awt.GraphicsConfiguration
   * @since 1.6
   */
  public int getYOnScreen() {
    return yAbs;
  }

  /**
   * Constructs a <code>MouseEvent</code> object with the
   * specified source component,
   * type, time, modifiers, coordinates, click count, popupTrigger flag,
   * and button number.
   * <p>
   * Creating an invalid event (such
   * as by using more than one of the old _MASKs, or modifier/button
   * values which don't match) results in unspecified behavior.
   * An invocation of the form
   * <tt>MouseEvent(source, id, when, modifiers, x, y, clickCount, popupTrigger, button)</tt>
   * behaves in exactly the same way as the invocation
   * <tt> {@link #MouseEvent(Component, int, long, int, int, int,
   * int, int, int, boolean, int) MouseEvent}(source, id, when, modifiers,
   * x, y, xAbs, yAbs, clickCount, popupTrigger, button)</tt>
   * where xAbs and yAbs defines as source's location on screen plus
   * relative coordinates x and y.
   * xAbs and yAbs are set to zero if the source is not showing.
   * This method throws an
   * <code>IllegalArgumentException</code> if <code>source</code>
   * is <code>null</code>.
   *
   * @param source The <code>Component</code> that originated the event
   * @param id An integer indicating the type of event. For information on allowable values, see the
   * class description for {@link MouseEvent}
   * @param when A long integer that gives the time the event occurred. Passing negative or zero
   * value is not recommended
   * @param modifiers a modifier mask describing the modifier keys and mouse buttons (for example,
   * shift, ctrl, alt, and meta) that are down during the event. Only extended modifiers are allowed
   * to be used as a value for this parameter (see the {@link InputEvent#getModifiersEx} class for
   * the description of extended modifiers). Passing negative parameter is not recommended. Zero
   * value means that no modifiers were passed
   * @param x The horizontal x coordinate for the mouse location. It is allowed to pass negative
   * values
   * @param y The vertical y coordinate for the mouse location. It is allowed to pass negative
   * values
   * @param clickCount The number of mouse clicks associated with event. Passing negative value is
   * not recommended
   * @param popupTrigger A boolean that equals {@code true} if this event is a trigger for a popup
   * menu
   * @param button An integer that indicates, which of the mouse buttons has changed its state. The
   * following rules are applied to this parameter: <ul> <li>If support for the extended mouse
   * buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java then it is allowed to
   * create {@code MouseEvent} objects only with the standard buttons: {@code NOBUTTON}, {@code
   * BUTTON1}, {@code BUTTON2}, and {@code BUTTON3}. <li> If support for the extended mouse buttons
   * is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java then it is allowed to create
   * {@code MouseEvent} objects with the standard buttons. In case the support for extended mouse
   * buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java, then in addition to
   * the standard buttons, {@code MouseEvent} objects can be created using buttons from the range
   * starting from 4 to {@link java.awt.MouseInfo#getNumberOfButtons()
   * MouseInfo.getNumberOfButtons()} if the mouse has more than three buttons. </ul>
   * @throws IllegalArgumentException if {@code button} is less then zero
   * @throws IllegalArgumentException if <code>source</code> is null
   * @throws IllegalArgumentException if {@code button} is greater then BUTTON3 and the support for
   * extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
   * @throws IllegalArgumentException if {@code button} is greater then the {@link
   * java.awt.MouseInfo#getNumberOfButtons() current number of buttons} and the support for extended
   * mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
   * @throws IllegalArgumentException if an invalid <code>button</code> value is passed in
   * @throws IllegalArgumentException if <code>source</code> is null
   * @see #getSource()
   * @see #getID()
   * @see #getWhen()
   * @see #getModifiers()
   * @see #getX()
   * @see #getY()
   * @see #getClickCount()
   * @see #isPopupTrigger()
   * @see #getButton()
   * @since 1.4
   */
  public MouseEvent(Component source, int id, long when, int modifiers,
      int x, int y, int clickCount, boolean popupTrigger,
      int button) {
    this(source, id, when, modifiers, x, y, 0, 0, clickCount, popupTrigger, button);
    Point eventLocationOnScreen = new Point(0, 0);
    try {
      eventLocationOnScreen = source.getLocationOnScreen();
      this.xAbs = eventLocationOnScreen.x + x;
      this.yAbs = eventLocationOnScreen.y + y;
    } catch (IllegalComponentStateException e) {
      this.xAbs = 0;
      this.yAbs = 0;
    }
  }

  /**
   * Constructs a <code>MouseEvent</code> object with the
   * specified source component,
   * type, modifiers, coordinates, click count, and popupTrigger flag.
   * An invocation of the form
   * <tt>MouseEvent(source, id, when, modifiers, x, y, clickCount, popupTrigger)</tt>
   * behaves in exactly the same way as the invocation
   * <tt> {@link #MouseEvent(Component, int, long, int, int, int,
   * int, int, int, boolean, int) MouseEvent}(source, id, when, modifiers,
   * x, y, xAbs, yAbs, clickCount, popupTrigger, MouseEvent.NOBUTTON)</tt>
   * where xAbs and yAbs defines as source's location on screen plus
   * relative coordinates x and y.
   * xAbs and yAbs are set to zero if the source is not showing.
   * This method throws an <code>IllegalArgumentException</code>
   * if <code>source</code> is <code>null</code>.
   *
   * @param source The <code>Component</code> that originated the event
   * @param id An integer indicating the type of event. For information on allowable values, see the
   * class description for {@link MouseEvent}
   * @param when A long integer that gives the time the event occurred. Passing negative or zero
   * value is not recommended
   * @param modifiers a modifier mask describing the modifier keys and mouse buttons (for example,
   * shift, ctrl, alt, and meta) that are down during the event. Only extended modifiers are allowed
   * to be used as a value for this parameter (see the {@link InputEvent#getModifiersEx} class for
   * the description of extended modifiers). Passing negative parameter is not recommended. Zero
   * value means that no modifiers were passed
   * @param x The horizontal x coordinate for the mouse location. It is allowed to pass negative
   * values
   * @param y The vertical y coordinate for the mouse location. It is allowed to pass negative
   * values
   * @param clickCount The number of mouse clicks associated with event. Passing negative value is
   * not recommended
   * @param popupTrigger A boolean that equals {@code true} if this event is a trigger for a popup
   * menu
   * @throws IllegalArgumentException if <code>source</code> is null
   * @see #getSource()
   * @see #getID()
   * @see #getWhen()
   * @see #getModifiers()
   * @see #getX()
   * @see #getY()
   * @see #getClickCount()
   * @see #isPopupTrigger()
   */
  public MouseEvent(Component source, int id, long when, int modifiers,
      int x, int y, int clickCount, boolean popupTrigger) {
    this(source, id, when, modifiers, x, y, clickCount, popupTrigger, NOBUTTON);
  }


  /* if the button is an extra button and it is released or clicked then in Xsystem its state
     is not modified. Exclude this button number from ExtModifiers mask.*/
  transient private boolean shouldExcludeButtonFromExtModifiers = false;

  /**
   * {@inheritDoc}
   */
  public int getModifiersEx() {
    int tmpModifiers = modifiers;
    if (shouldExcludeButtonFromExtModifiers) {
      tmpModifiers &= ~(InputEvent.getMaskForButton(getButton()));
    }
    return tmpModifiers & ~JDK_1_3_MODIFIERS;
  }

  /**
   * Constructs a <code>MouseEvent</code> object with the
   * specified source component,
   * type, time, modifiers, coordinates, absolute coordinates, click count, popupTrigger flag,
   * and button number.
   * <p>
   * Creating an invalid event (such
   * as by using more than one of the old _MASKs, or modifier/button
   * values which don't match) results in unspecified behavior.
   * Even if inconsistent values for relative and absolute coordinates are
   * passed to the constructor, the mouse event instance is still
   * created and no exception is thrown.
   * This method throws an
   * <code>IllegalArgumentException</code> if <code>source</code>
   * is <code>null</code>.
   *
   * @param source The <code>Component</code> that originated the event
   * @param id An integer indicating the type of event. For information on allowable values, see the
   * class description for {@link MouseEvent}
   * @param when A long integer that gives the time the event occurred. Passing negative or zero
   * value is not recommended
   * @param modifiers a modifier mask describing the modifier keys and mouse buttons (for example,
   * shift, ctrl, alt, and meta) that are down during the event. Only extended modifiers are allowed
   * to be used as a value for this parameter (see the {@link InputEvent#getModifiersEx} class for
   * the description of extended modifiers). Passing negative parameter is not recommended. Zero
   * value means that no modifiers were passed
   * @param x The horizontal x coordinate for the mouse location. It is allowed to pass negative
   * values
   * @param y The vertical y coordinate for the mouse location. It is allowed to pass negative
   * values
   * @param xAbs The absolute horizontal x coordinate for the mouse location It is allowed to pass
   * negative values
   * @param yAbs The absolute vertical y coordinate for the mouse location It is allowed to pass
   * negative values
   * @param clickCount The number of mouse clicks associated with event. Passing negative value is
   * not recommended
   * @param popupTrigger A boolean that equals {@code true} if this event is a trigger for a popup
   * menu
   * @param button An integer that indicates, which of the mouse buttons has changed its state. The
   * following rules are applied to this parameter: <ul> <li>If support for the extended mouse
   * buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java then it is allowed to
   * create {@code MouseEvent} objects only with the standard buttons: {@code NOBUTTON}, {@code
   * BUTTON1}, {@code BUTTON2}, and {@code BUTTON3}. <li> If support for the extended mouse buttons
   * is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java then it is allowed to create
   * {@code MouseEvent} objects with the standard buttons. In case the support for extended mouse
   * buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java, then in addition to
   * the standard buttons, {@code MouseEvent} objects can be created using buttons from the range
   * starting from 4 to {@link java.awt.MouseInfo#getNumberOfButtons()
   * MouseInfo.getNumberOfButtons()} if the mouse has more than three buttons. </ul>
   * @throws IllegalArgumentException if {@code button} is less then zero
   * @throws IllegalArgumentException if <code>source</code> is null
   * @throws IllegalArgumentException if {@code button} is greater then BUTTON3 and the support for
   * extended mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
   * @throws IllegalArgumentException if {@code button} is greater then the {@link
   * java.awt.MouseInfo#getNumberOfButtons() current number of buttons} and the support for extended
   * mouse buttons is {@link Toolkit#areExtraMouseButtonsEnabled() enabled} by Java
   * @throws IllegalArgumentException if an invalid <code>button</code> value is passed in
   * @throws IllegalArgumentException if <code>source</code> is null
   * @see #getSource()
   * @see #getID()
   * @see #getWhen()
   * @see #getModifiers()
   * @see #getX()
   * @see #getY()
   * @see #getXOnScreen()
   * @see #getYOnScreen()
   * @see #getClickCount()
   * @see #isPopupTrigger()
   * @see #getButton()
   * @see #button
   * @see Toolkit#areExtraMouseButtonsEnabled()
   * @see java.awt.MouseInfo#getNumberOfButtons()
   * @see InputEvent#getMaskForButton(int)
   * @since 1.6
   */
  public MouseEvent(Component source, int id, long when, int modifiers,
      int x, int y, int xAbs, int yAbs,
      int clickCount, boolean popupTrigger, int button) {
    super(source, id, when, modifiers);
    this.x = x;
    this.y = y;
    this.xAbs = xAbs;
    this.yAbs = yAbs;
    this.clickCount = clickCount;
    this.popupTrigger = popupTrigger;
    if (button < NOBUTTON) {
      throw new IllegalArgumentException("Invalid button value :" + button);
    }
    if (button > BUTTON3) {
      if (!Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled()) {
        throw new IllegalArgumentException("Extra mouse events are disabled " + button);
      } else {
        if (button > cachedNumberOfButtons) {
          throw new IllegalArgumentException("Nonexistent button " + button);
        }
      }
      // XToolkit: extra buttons are not reporting about their state correctly.
      // Being pressed they report the state=0 both on the press and on the release.
      // For 1-3 buttons the state value equals zero on press and non-zero on release.
      // Other modifiers like Shift, ALT etc seem report well with extra buttons.
      // The problem reveals as follows: one button is pressed and then another button is pressed and released.
      // So, the getModifiersEx() would not be zero due to a first button and we will skip this modifier.
      // This may have to be moved into the peer code instead if possible.

      if (getModifiersEx() != 0) { //There is at least one more button in a pressed state.
        if (id == MouseEvent.MOUSE_RELEASED || id == MouseEvent.MOUSE_CLICKED) {
          shouldExcludeButtonFromExtModifiers = true;
        }
      }
    }

    this.button = button;

    if ((getModifiers() != 0) && (getModifiersEx() == 0)) {
      setNewModifiers();
    } else if ((getModifiers() == 0) &&
        (getModifiersEx() != 0 || button != NOBUTTON) &&
        (button <= BUTTON3)) {
      setOldModifiers();
    }
  }

  /**
   * Returns the horizontal x position of the event relative to the
   * source component.
   *
   * @return x  an integer indicating horizontal position relative to the component
   */
  public int getX() {
    return x;
  }

  /**
   * Returns the vertical y position of the event relative to the
   * source component.
   *
   * @return y  an integer indicating vertical position relative to the component
   */
  public int getY() {
    return y;
  }

  /**
   * Returns the x,y position of the event relative to the source component.
   *
   * @return a <code>Point</code> object containing the x and y coordinates relative to the source
   * component
   */
  public Point getPoint() {
    int x;
    int y;
    synchronized (this) {
      x = this.x;
      y = this.y;
    }
    return new Point(x, y);
  }

  /**
   * Translates the event's coordinates to a new position
   * by adding specified <code>x</code> (horizontal) and <code>y</code>
   * (vertical) offsets.
   *
   * @param x the horizontal x value to add to the current x coordinate position
   * @param y the vertical y value to add to the current y coordinate position
   */
  public synchronized void translatePoint(int x, int y) {
    this.x += x;
    this.y += y;
  }

  /**
   * Returns the number of mouse clicks associated with this event.
   *
   * @return integer value for the number of clicks
   */
  public int getClickCount() {
    return clickCount;
  }

  /**
   * Returns which, if any, of the mouse buttons has changed state. The returned value is ranged
   * from 0 to the {@link java.awt.MouseInfo#getNumberOfButtons() MouseInfo.getNumberOfButtons()}
   * value. The returned value includes at least the following constants: <ul> <li> {@code NOBUTTON}
   * <li> {@code BUTTON1} <li> {@code BUTTON2} <li> {@code BUTTON3} </ul> It is allowed to use those
   * constants to compare with the returned button number in the application. For example,
   * <pre>
   * if (anEvent.getButton() == MouseEvent.BUTTON1) {
   * </pre>
   * In particular, for a mouse with one, two, or three buttons this method may return the following
   * values: <ul> <li> 0 ({@code NOBUTTON}) <li> 1 ({@code BUTTON1}) <li> 2 ({@code BUTTON2}) <li> 3
   * ({@code BUTTON3}) </ul> Button numbers greater then {@code BUTTON3} have no constant
   * identifier. So if a mouse with five buttons is installed, this method may return the following
   * values: <ul> <li> 0 ({@code NOBUTTON}) <li> 1 ({@code BUTTON1}) <li> 2 ({@code BUTTON2}) <li> 3
   * ({@code BUTTON3}) <li> 4 <li> 5 </ul> <p> Note: If support for extended mouse buttons is {@link
   * Toolkit#areExtraMouseButtonsEnabled() disabled} by Java then the AWT event subsystem does not
   * produce mouse events for the extended mouse buttons. So it is not expected that this method
   * returns anything except {@code NOBUTTON}, {@code BUTTON1}, {@code BUTTON2}, {@code BUTTON3}.
   *
   * @return one of the values from 0 to {@link java.awt.MouseInfo#getNumberOfButtons()
   * MouseInfo.getNumberOfButtons()} if support for the extended mouse buttons is {@link
   * Toolkit#areExtraMouseButtonsEnabled() enabled} by Java. That range includes {@code NOBUTTON},
   * {@code BUTTON1}, {@code BUTTON2}, {@code BUTTON3}; <br> {@code NOBUTTON}, {@code BUTTON1},
   * {@code BUTTON2} or {@code BUTTON3} if support for the extended mouse buttons is {@link
   * Toolkit#areExtraMouseButtonsEnabled() disabled} by Java
   * @see Toolkit#areExtraMouseButtonsEnabled()
   * @see java.awt.MouseInfo#getNumberOfButtons()
   * @see #MouseEvent(Component, int, long, int, int, int, int, int, int, boolean, int)
   * @see InputEvent#getMaskForButton(int)
   * @since 1.4
   */
  public int getButton() {
    return button;
  }

  /**
   * Returns whether or not this mouse event is the popup menu
   * trigger event for the platform.
   * <p><b>Note</b>: Popup menus are triggered differently
   * on different systems. Therefore, <code>isPopupTrigger</code>
   * should be checked in both <code>mousePressed</code>
   * and <code>mouseReleased</code>
   * for proper cross-platform functionality.
   *
   * @return boolean, true if this event is the popup menu trigger for this platform
   */
  public boolean isPopupTrigger() {
    return popupTrigger;
  }

  /**
   * Returns a <code>String</code> instance describing the modifier keys and
   * mouse buttons that were down during the event, such as "Shift",
   * or "Ctrl+Shift". These strings can be localized by changing
   * the <code>awt.properties</code> file.
   * <p>
   * Note that the <code>InputEvent.ALT_MASK</code> and
   * <code>InputEvent.BUTTON2_MASK</code> have equal values,
   * so the "Alt" string is returned for both modifiers.  Likewise,
   * the <code>InputEvent.META_MASK</code> and
   * <code>InputEvent.BUTTON3_MASK</code> have equal values,
   * so the "Meta" string is returned for both modifiers.
   * <p>
   * Note that passing negative parameter is incorrect,
   * and will cause the returning an unspecified string.
   * Zero parameter means that no modifiers were passed and will
   * cause the returning an empty string.
   * <p>
   *
   * @param modifiers A modifier mask describing the modifier keys and mouse buttons that were down
   * during the event
   * @return string   string text description of the combination of modifier keys and mouse buttons
   * that were down during the event
   * @see InputEvent#getModifiersExText(int)
   * @since 1.4
   */
  public static String getMouseModifiersText(int modifiers) {
    StringBuilder buf = new StringBuilder();
    if ((modifiers & InputEvent.ALT_MASK) != 0) {
      buf.append(Toolkit.getProperty("AWT.alt", "Alt"));
      buf.append("+");
    }
    if ((modifiers & InputEvent.META_MASK) != 0) {
      buf.append(Toolkit.getProperty("AWT.meta", "Meta"));
      buf.append("+");
    }
    if ((modifiers & InputEvent.CTRL_MASK) != 0) {
      buf.append(Toolkit.getProperty("AWT.control", "Ctrl"));
      buf.append("+");
    }
    if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
      buf.append(Toolkit.getProperty("AWT.shift", "Shift"));
      buf.append("+");
    }
    if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) {
      buf.append(Toolkit.getProperty("AWT.altGraph", "Alt Graph"));
      buf.append("+");
    }
    if ((modifiers & InputEvent.BUTTON1_MASK) != 0) {
      buf.append(Toolkit.getProperty("AWT.button1", "Button1"));
      buf.append("+");
    }
    if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
      buf.append(Toolkit.getProperty("AWT.button2", "Button2"));
      buf.append("+");
    }
    if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
      buf.append(Toolkit.getProperty("AWT.button3", "Button3"));
      buf.append("+");
    }

    int mask;

    // TODO: add a toolkit field that holds a number of button on the mouse.
    // As the method getMouseModifiersText() is static and obtain
    // an integer as a parameter then we may not restrict this with the number
    // of buttons installed on the mouse.
    // It's a temporary solution. We need to somehow hold the number of buttons somewhere else.
    for (int i = 1; i <= cachedNumberOfButtons; i++) {
      mask = InputEvent.getMaskForButton(i);
      if ((modifiers & mask) != 0 &&
          buf.indexOf(Toolkit.getProperty("AWT.button" + i, "Button" + i))
              == -1) //1,2,3 buttons may already be there; so don't duplicate it.
      {
        buf.append(Toolkit.getProperty("AWT.button" + i, "Button" + i));
        buf.append("+");
      }
    }

    if (buf.length() > 0) {
      buf.setLength(buf.length() - 1); // remove trailing '+'
    }
    return buf.toString();
  }

  /**
   * Returns a parameter string identifying this event.
   * This method is useful for event-logging and for debugging.
   *
   * @return a string identifying the event and its attributes
   */
  public String paramString() {
    StringBuilder str = new StringBuilder(80);

    switch (id) {
      case MOUSE_PRESSED:
        str.append("MOUSE_PRESSED");
        break;
      case MOUSE_RELEASED:
        str.append("MOUSE_RELEASED");
        break;
      case MOUSE_CLICKED:
        str.append("MOUSE_CLICKED");
        break;
      case MOUSE_ENTERED:
        str.append("MOUSE_ENTERED");
        break;
      case MOUSE_EXITED:
        str.append("MOUSE_EXITED");
        break;
      case MOUSE_MOVED:
        str.append("MOUSE_MOVED");
        break;
      case MOUSE_DRAGGED:
        str.append("MOUSE_DRAGGED");
        break;
      case MOUSE_WHEEL:
        str.append("MOUSE_WHEEL");
        break;
      default:
        str.append("unknown type");
    }

    // (x,y) coordinates
    str.append(",(").append(x).append(",").append(y).append(")");
    str.append(",absolute(").append(xAbs).append(",").append(yAbs).append(")");

    if (id != MOUSE_DRAGGED && id != MOUSE_MOVED) {
      str.append(",button=").append(getButton());
    }

    if (getModifiers() != 0) {
      str.append(",modifiers=").append(getMouseModifiersText(modifiers));
    }

    if (getModifiersEx() != 0) {
      //Using plain "modifiers" here does show an excluded extended buttons in the string event representation.
      //getModifiersEx() solves the problem.
      str.append(",extModifiers=").append(getModifiersExText(getModifiersEx()));
    }

    str.append(",clickCount=").append(clickCount);

    return str.toString();
  }

  /**
   * Sets new modifiers by the old ones.
   * Also sets button.
   */
  private void setNewModifiers() {
    if ((modifiers & BUTTON1_MASK) != 0) {
      modifiers |= BUTTON1_DOWN_MASK;
    }
    if ((modifiers & BUTTON2_MASK) != 0) {
      modifiers |= BUTTON2_DOWN_MASK;
    }
    if ((modifiers & BUTTON3_MASK) != 0) {
      modifiers |= BUTTON3_DOWN_MASK;
    }
    if (id == MOUSE_PRESSED
        || id == MOUSE_RELEASED
        || id == MOUSE_CLICKED) {
      if ((modifiers & BUTTON1_MASK) != 0) {
        button = BUTTON1;
        modifiers &= ~BUTTON2_MASK & ~BUTTON3_MASK;
        if (id != MOUSE_PRESSED) {
          modifiers &= ~BUTTON1_DOWN_MASK;
        }
      } else if ((modifiers & BUTTON2_MASK) != 0) {
        button = BUTTON2;
        modifiers &= ~BUTTON1_MASK & ~BUTTON3_MASK;
        if (id != MOUSE_PRESSED) {
          modifiers &= ~BUTTON2_DOWN_MASK;
        }
      } else if ((modifiers & BUTTON3_MASK) != 0) {
        button = BUTTON3;
        modifiers &= ~BUTTON1_MASK & ~BUTTON2_MASK;
        if (id != MOUSE_PRESSED) {
          modifiers &= ~BUTTON3_DOWN_MASK;
        }
      }
    }
    if ((modifiers & InputEvent.ALT_MASK) != 0) {
      modifiers |= InputEvent.ALT_DOWN_MASK;
    }
    if ((modifiers & InputEvent.META_MASK) != 0) {
      modifiers |= InputEvent.META_DOWN_MASK;
    }
    if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
      modifiers |= InputEvent.SHIFT_DOWN_MASK;
    }
    if ((modifiers & InputEvent.CTRL_MASK) != 0) {
      modifiers |= InputEvent.CTRL_DOWN_MASK;
    }
    if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) {
      modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
    }
  }

  /**
   * Sets old modifiers by the new ones.
   */
  private void setOldModifiers() {
    if (id == MOUSE_PRESSED
        || id == MOUSE_RELEASED
        || id == MOUSE_CLICKED) {
      switch (button) {
        case BUTTON1:
          modifiers |= BUTTON1_MASK;
          break;
        case BUTTON2:
          modifiers |= BUTTON2_MASK;
          break;
        case BUTTON3:
          modifiers |= BUTTON3_MASK;
          break;
      }
    } else {
      if ((modifiers & BUTTON1_DOWN_MASK) != 0) {
        modifiers |= BUTTON1_MASK;
      }
      if ((modifiers & BUTTON2_DOWN_MASK) != 0) {
        modifiers |= BUTTON2_MASK;
      }
      if ((modifiers & BUTTON3_DOWN_MASK) != 0) {
        modifiers |= BUTTON3_MASK;
      }
    }
    if ((modifiers & ALT_DOWN_MASK) != 0) {
      modifiers |= ALT_MASK;
    }
    if ((modifiers & META_DOWN_MASK) != 0) {
      modifiers |= META_MASK;
    }
    if ((modifiers & SHIFT_DOWN_MASK) != 0) {
      modifiers |= SHIFT_MASK;
    }
    if ((modifiers & CTRL_DOWN_MASK) != 0) {
      modifiers |= CTRL_MASK;
    }
    if ((modifiers & ALT_GRAPH_DOWN_MASK) != 0) {
      modifiers |= ALT_GRAPH_MASK;
    }
  }

  /**
   * Sets new modifiers by the old ones.
   *
   * @serial
   */
  private void readObject(ObjectInputStream s)
      throws IOException, ClassNotFoundException {
    s.defaultReadObject();
    if (getModifiers() != 0 && getModifiersEx() == 0) {
      setNewModifiers();
    }
  }
}
